package com.lysb.core.utils;

import org.dom4j.*;
import org.dom4j.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.xml.bind.*;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Component
public class XmlUtil {

    private static Validator validator;
    @Autowired
    public void setValidator(Validator validator) {
        XmlUtil.validator = validator;
    }

    public static Map<String, Object> parseXml(String xmlString) throws DocumentException {
        Document document = DocumentHelper.parseText(xmlString);
        Element hosElement = document.getRootElement();
        Map<String, Object> map = parseXml(hosElement);
        return map;
    }

    public static Map<String, Object> parseXml(Element element) {
        Map<String, Object> result = new HashMap<>();
        Map<String, Object> attributes = new HashMap<>();

        // 属性
        for (Object o1 : element.attributes()) {
            Attribute att = (Attribute) o1;
            attributes.put(att.getName(), att.getStringValue());
        }

        // 子元素
        for (Object o : element.elements()) {
            Element ele = (Element) o;
            attributes.putAll(parseXml(ele));
        }

        if (attributes.size() > 0) {
            result.put(element.getName(), attributes);
        } else {
            result.put(element.getName(), element.getStringValue());
        }

        return result;
    }

    /**
     * 将对象直接转换成String类型的 XML输出
     *
     * @param obj
     * @return
     */
    public static String bean2xml(Object obj) {

        // 创建输出流
        StringWriter sw = new StringWriter();
        sw.append("<?xml version=\"1.0\" encoding=\"GBK\"?>\n");
        try {
            // 利用jdk中自带的转换类实现
            JAXBContext context = JAXBContext.newInstance(obj.getClass());
            Marshaller marshaller = context.createMarshaller();
            // 格式化xml输出的格式
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
//            marshaller.setProperty(Marshaller.JAXB_ENCODING, "GBK");
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
            marshaller.setListener(new Marshaller.Listener() {
                @Override
                public void beforeMarshal(Object source) {
                    Field[] fields = source.getClass().getDeclaredFields();
                    for (Field f : fields) {
                        f.setAccessible(true);
                        try {
                            //对象为空且类型为String时候设置空值
                            if (f.getType() == String.class && f.get(source) == null) {
                                f.set(source, "");
                            }
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            // 将对象转换成输出流形式的xml
            marshaller.marshal(obj, sw);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return sw.toString();
    }

    /**
     * 将String类型的xml转换成对象
     */
    public static <T> Object xml2bean(String xmlStr, Class<T> clazz) throws JAXBException {

            JAXBContext context = JAXBContext.newInstance(clazz);
            // 进行将Xml转成对象的核心接口
            Unmarshaller unmarshaller = context.createUnmarshaller();
            StringReader sr = new StringReader(xmlStr);
            return unmarshaller.unmarshal(sr);
    }

    public static <T> Object xml2beanWithValid(String xmlstr, Class<T> clazz) {
        T bean = null;
        try {
            bean = (T)xml2bean(xmlstr, clazz);
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
        Set<ConstraintViolation<T>> validate = validator.validate(bean);
        if (!validate.isEmpty()){
            throw new RuntimeException(validate.iterator().next().getMessage());
        }
        return bean;
    }

}
